import docker
import tornado
import requests
import json
from tornado import gen
from tornado.options import options

class DockerImageUtil:
    @staticmethod
    def get_all_images():
        results = []
        repositories_url = tornado.options.options.docker_registry_url + '/v2/_catalog'
        repositories = requests.get(repositories_url).json()['repositories']
        for repository in repositories:
            image_url = tornado.options.options.docker_registry_url + '/v2' + repository + '/tags/list'
            image = requests.get(image_url).json()
            for image_tag in image['tags']:
                results.append(image['name'] + ":" + image_tag)
        return results
    
    # def save_image(self, image_full_name, tar_full_name):
    #     ''' 
    #     将当前容器服务器镜像库中的指定镜像保存到一个tar文件中
    #         *image_full_name：镜像名称:Tag
    #         *tar_full_name：包含全路径的导出文件，一般以.tar为后缀
    #     '''
    #     docker_client = docker.DockerClient(base_url=self.api_url)
    #     image = docker_client.images.get(image_full_name)
    #     chunks = image.save()
    #     file = open(tar_full_name, 'wb')
    #     for chunk in chunks:
    #         file.write(chunk)
    #     file.close()
    #     docker_client.close()

    # def load_image(self, tar_full_name):
    #     '''
    #     将tar文件导入到当前容器服务器镜像库中
    #         *tar_full_name：包含全路径的导出文件，一般以.tar为后缀
    #     '''
    #     docker_client = docker.DockerClient(base_url=self.api_url)
    #     with open(tar_full_name, 'rb') as file:
    #         docker_client.images.load(file)
    #     docker_client.close()

    # def tag_and_push(self, image_name, image_tag='latest', new_tag='latest'):
    #     '''
    #     将当前容器服务器镜像库中的镜像push到本地私有repository中
    #     '''
    #     docker_client = docker.DockerClient(base_url=self.api_url)
    #     origin_image_full_name = str.format("{}:{}", image_name, image_tag)
    #     image = docker_client.images.get(origin_image_full_name)
    #     new_image_name = str.format("{}/{}", self.registry_url, image_name)
    #     image.tag(new_image_name, new_tag)
    #     docker_client.images.push(new_image_name, tag=new_tag)
    #     docker_client.close()

class DockerServiceUtil:
    @staticmethod
    def get_all_services():
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        try:
            # 填充节点关键信息备用
            nodes = []
            for item in docker_client.nodes.list():
                node = {
                    'id' : item.short_id,
                    'hostname' : item.attrs['Description']['Hostname'],
                    'ip' : item.attrs['Status']['Addr']
                }
                nodes.append(node)

            # 获取Service信息
            results = []
            for item in docker_client.services.list():
                task = item.tasks({'desired-state':'running'})[0]
                task_nodeid = task['NodeID'][:10]
                tasknode = [node for node in nodes if node['id']==task_nodeid][0]
                service = {
                    'servicename' : item.name,
                    'createdtime' : item.attrs['CreatedAt'],
                    'hostname' : tasknode['hostname'],
                    'ip' : tasknode['ip'],
                    'state' : task['Status']['State']
                }
                results.append(service)

            return results
        finally:
            docker_client.close()
    

    @staticmethod
    def remove_service(service_name):
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        try:
            service = docker_client.services.get(service_name)
            service.remove()
        # except docker.errors.NotFound as ex:
        #     return
        finally:
            docker_client.close()


class JupyterServiceUtil:
    @staticmethod
    def build_jupyter_service_name(accountname, imagename):
        return 'jupyter-' + imagename.replace(':', '-') + '-' + accountname
    
    @staticmethod
    def get_image_full_name(image_name):
        return tornado.options.options.docker_registry_url + "/" + image_name

    @staticmethod
    def create_service(service_name, image_full_name, mount_host_path):
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        endpoint_spec = {'Mode':'vip', 'Ports':[{'Protocal':'tcp', 'PublishMode':'ingress', 'TargetPort':tornado.options.options.jupyter_target_port}]}
        mount = '{}:{}:rw'.format(mount_host_path, tornado.options.options.jupyter_workspace_path)
        try:
            service = docker_client.services.create(
                image = image_full_name,
                args = tornado.options.options.jupyter_startup_args,
                endpoint_spec = endpoint_spec,
                mounts = [mount], 
                name = service_name)
            return True
        except docker.errors.APIError as ex:
            print(ex)
            return False
        finally:
            docker_client.close()

    @staticmethod
    def get_service_state_and_port(service_name):
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        try:
            service = docker_client.services.get(service_name)
            # 取tasks中当前状态未running的那一个task，其对应的port为当前container的port
            # 如果在没有关闭servcie的情况下重启了服务器，则重启后该service将自动重建，并且建立一个新的task。原先的task中的State改为:complete
            desired_state = 'running'
            running_tasks = service.tasks({'desired-state':desired_state})
            if len(running_tasks) == 0:
                return ("none", 0)
            # state = running_tasks[0]["Status"]['State']                  
            port = service.attrs['Endpoint']['Ports'][0]['PublishedPort']
            return (desired_state, port)
        except docker.errors.NotFound as ex:
            print(ex)
            return ("none", 0)
        finally:
            docker_client.close()

    @staticmethod
    def remove_service(service_name):
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        try:
            service = docker_client.services.get(service_name)
            service.remove()
        # except docker.errors.NotFound as ex:
        #     return
        finally:
            docker_client.close()
    
    @staticmethod
    def find_user_jupyter_services(accountname):
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        try:
            services = docker_client.services.list()
            for service in services:
                service_name = service.attrs['Spec']['Name']
                if service_name.startswith('jupyter') and service_name.find(accountname) > 0:
                    return service_name, service.attrs['Endpoint']['Ports'][0]['PublishedPort']                    
            return (None, 0)
        finally:
            docker_client.close()
    
    @staticmethod
    def find_user_all_services(accountname):
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        user_services = []
        try:
            services = docker_client.services.list()
            for service in services:
                service_name = service.attrs['Spec']['Name']
                if service_name.find(accountname) > 0:
                    user_services.append(service_name)            
        finally:
            docker_client.close()
        
        return user_services
    

# 虚拟桌面开发环境容器管理
class VirtualEnvUtil:
    @staticmethod
    def build_virtual_env_service_name(accountname, imagename):
        return 'virtualenv-' + imagename.replace(':', '-') + '-' + accountname

    @staticmethod
    def create_service(service_name, image_full_name, ports, mounts, params):
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)

        # [80, 5900]
        ports_spec = []
        for port in ports:
            ports_spec.append({
                'Protocal' : 'tcp', 
                'PublishMode' : 'ingress', 
                'TargetPort' : port
                })
        endpoint_spec = {'Mode':'vip', 'Ports':ports_spec}

        # [{'source':'/xxx1', 'target':'/yyy1'}, {'source':'/xxx2', 'target':'/yyy2'}]
        mounts_spec = []
        for mount in mounts:
            mounts_spec.append(
                '{}:{}:rw'.format(mount['source'], mount['target'])
            )

        try:
            docker_client.services.create(
                image = image_full_name,                
                endpoint_spec = endpoint_spec,
                mounts = mounts_spec, 
                env = params,                       # ['RESOLUTION=1920x1080','VNC_PASSWORD=111111']
                name = service_name)
            return True
        # except docker.errors.APIError as ex:
        except:
            return False
        finally:
            docker_client.close()
    
    @staticmethod
    def service_exists(service_name):
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        try:
            docker_client.services.get(service_name)
            return True
        except docker.errors.NotFound as ex:
            return False
        finally:
            docker_client.close()

    @staticmethod
    def get_service_state_and_port(service_name):
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        try:
            service = docker_client.services.get(service_name)
            desired_state = 'running'
            running_tasks = service.tasks({'desired-state':desired_state})
            if len(running_tasks) == 0:
                return ("", 0)
            port = service.attrs['Endpoint']['Ports'][0]['PublishedPort']
            return (desired_state, port)
        except docker.errors.NotFound as ex:
            print(ex)
            return ("", 0)
        finally:
            docker_client.close()


# 容器集群服务器管理
class DockerServerUtil:
    @staticmethod
    def get_all_servers():
        docker_client = docker.DockerClient(base_url=tornado.options.options.docker_api_url)
        try:
            results = []
            nodes = docker_client.nodes.list()
            for node in nodes:
                nodeinfo = {
                    'id' : node.attrs['ID'],
                    'hostname' : node.attrs['Description']['Hostname'],
                    'ip' : node.attrs['Status']['Addr'],
                    'state' : node.attrs['Status']['State'],
                    'role' : node.attrs['Spec']['Role'],
                    'cpu' : node.attrs['Description']['Resources']["NanoCPUs"],
                    'memory' : node.attrs['Description']['Resources']['MemoryBytes'],
                    'platform' : node.attrs['Description']['Platform']['OS'],
                    'engineversion' : node.attrs['Description']['Engine']['EngineVersion'],
                    'createdAt' : node.attrs['CreatedAt']
                }
                results.append(nodeinfo)
            
            return results
        finally:
            docker_client.close()